﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.UI;

namespace Morus.Presentation
{
    public class PageViewHost
    {
        private readonly PresenterBinder _presenterBinder;
        private readonly static string _viewHostCacheKey = typeof(PageViewHost).FullName + ".PageContextKey";

        public PageViewHost(Page page, HttpContextBase httpContext)
        {
            Check.Argument.IsNotNull(page, "page");
            Check.Argument.IsNotNull(httpContext, "httpContext");

            var hosts = FindHosts(page).ToArray();

            _presenterBinder = new PresenterBinder(hosts);

            // Subscribing PageViewHost to Page.InitComplete event.");
            page.InitComplete += Page_InitComplete;

            // Subscribing PageViewHost to Page.PreRenderComplete event.");
            page.PreRenderComplete += Page_PreRenderComplete;

            // Subscribing PageViewHost to Page.Unload event.");
            page.Unload += Page_Unload;
        }

        private void RegisterView(IView view)
        {
            _presenterBinder.RegisterView(view);
        }

        void Page_InitComplete(object sender, EventArgs e)
        {
            _presenterBinder.PerformBinding();
        }

        void Page_PreRenderComplete(object sender, EventArgs e)
        {
            _presenterBinder.MessageCoordinator.Close();
        }

        void Page_Unload(object sender, EventArgs e)
        {
            _presenterBinder.Release();
        }

        internal static IEnumerable<object> FindHosts(Page page)
        {
            yield return page;

            var masterHost = page.Master;
            while (masterHost != null)
            {
                yield return masterHost;
                masterHost = masterHost.Master;
            }
        }

        internal static PageViewHost FindViewHost(Control control, HttpContextBase httpContext)
        {
            var pageContext = control.Page.Items;

            if (pageContext.Contains(_viewHostCacheKey))
            {
                return (PageViewHost)pageContext[_viewHostCacheKey];
            }
            var host = new PageViewHost(control.Page, httpContext);

            pageContext[_viewHostCacheKey] = host;

            return host;
        }

        public static void Register<T>(T control, HttpContextBase httpContext, bool enableAutomaticDataBinding)
            where T : Control, IView
        {
            Check.Argument.IsNotNull(control, "control");
            Check.Argument.IsNotNull(httpContext, "httpContext");

            if (control.Page == null)
            {
                throw new InvalidOperationException("Controls can only be registered once they have been added to the live control tree. The best place to register them is within the control's Init event.");
            }

            var viewHost = FindViewHost(control, httpContext);
            viewHost.RegisterView(control);

            // This event is raised after any async page tasks have completed, so it is safe to data-bind
            if (enableAutomaticDataBinding)
            {
                control.Page.PreRenderComplete += (sender, e) => control.DataBind();
            }
        }
    }
}